/*
 * Copyright 2015 Hannes Dorfmann.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *    http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.hannesdorfmann.mosby3.mvp.delegate;


import com.hannesdorfmann.mosby3.mvp.MvpAbility;
import ohos.aafwk.ability.Ability;
import ohos.aafwk.content.Intent;
import ohos.hiviewdfx.HiLog;
import ohos.hiviewdfx.HiLogLabel;
import ohos.utils.PacMap;

import com.hannesdorfmann.mosby3.PresenterManager;
import com.hannesdorfmann.mosby3.mvp.MvpPresenter;
import com.hannesdorfmann.mosby3.mvp.MvpView;

import java.util.UUID;

/**
 * The concrete implementation of {@link}
 *
 * @param <V> The type of {@link MvpView}
 * @param <P> The type of {@link MvpPresenter}
 * @author Hannes Dorfmann
 * @see AbilityMvpDelegate
 */
public class AbilityMvpDelegateImpl<V extends MvpView, P extends MvpPresenter<V>>
        implements AbilityMvpDelegate {

    protected static final String KEY_MOSBY_VIEW_ID = "com.hannesdorfmann.mosby3.activity.mvp.id";

    public final static boolean DEBUG = false;
    private static final String TAG = "AbilityMvpDelegateImpl";
    private static final HiLogLabel LABEL = new HiLogLabel(HiLog.LOG_APP, 0, TAG);

    private MvpDelegateCallback<V, P> delegateCallback;
    protected boolean keepPresenterInstance;
    protected Ability activity;
    protected String mosbyViewId = null;

  /**
   * @param activity The Ability
   * @param delegateCallback The callback
   * @param keepPresenterInstance true, if the presenter instance should be kept across screen
   * orientation changes. Otherwise false.
   */
  public AbilityMvpDelegateImpl(Ability activity,
      MvpDelegateCallback<V, P> delegateCallback, boolean keepPresenterInstance) {

        if (activity == null) {
            throw new NullPointerException("Activity is null!");
        }

        if (delegateCallback == null) {
            throw new NullPointerException("MvpDelegateCallback is null!");
        }
        this.delegateCallback = delegateCallback;
        this.activity = activity;
        this.keepPresenterInstance = keepPresenterInstance;
    }

    /**
     * Determines whether or not a Presenter Instance should be kept
     *
     * @param keepPresenterInstance true, if the delegate has enabled keep
     */
    static boolean retainPresenterInstance(boolean keepPresenterInstance, Ability activity) {
        return keepPresenterInstance && (activity.isUpdatingConfigurations() || !activity.isTerminating());
    }

    /**
     * Generates the unique (mosby internal) view id and calls {@link
     * MvpDelegateCallback#createPresenter()}
     * to create a new presenter instance
     *
     * @return The new created presenter instance
     */
    private P createViewIdAndCreatePresenter() {
        P presenter = delegateCallback.createPresenter();
        if (presenter == null) {
            throw new NullPointerException(
                    "Presenter returned from createPresenter() is null. Activity is " + activity);
        }
        if (keepPresenterInstance) {
            mosbyViewId = UUID.randomUUID().toString();
            PresenterManager.putPresenter(activity, mosbyViewId, presenter);
        }
        return presenter;
    }

    private P getPresenter() {
        P presenter = delegateCallback.getPresenter();
        if (presenter == null) {
            throw new NullPointerException("Presenter returned from getPresenter() is null");
        }
        return presenter;
    }

    private V getMvpView() {
        V view = delegateCallback.getMvpView();
        if (view == null) {
            throw new NullPointerException("View returned from getMvpView() is null");
        }
        return view;
    }

    @Override
    public void onStart(Intent intent) {
    }

    @Override
    public void onPostStart(PacMap pacMap) {
    }

    @Override
    public void onStop() {
        boolean retainPresenterInstance = retainPresenterInstance(keepPresenterInstance, activity);
        getPresenter().detachView();
        if (!retainPresenterInstance) {
            getPresenter().destroy();
        }
        if (!retainPresenterInstance && mosbyViewId != null) {
            PresenterManager.remove(activity, mosbyViewId);
        }

        if (DEBUG) {
            if (retainPresenterInstance) {
                HiLog.error(LABEL, "View"
                        + getMvpView()
                        + " destroyed temporarily. View detached from presenter "
                        + getPresenter());
            } else {
                HiLog.error(LABEL, "View"
                        + getMvpView()
                        + " destroyed permanently. View detached permanently from presenter "
                        + getPresenter());
            }
        }

    }

    @Override
    public void onActive() {
        P presenter = null;

        if (keepPresenterInstance) {

            if (DEBUG) {
                HiLog.error(LABEL,
                        "MosbyView ID = " + mosbyViewId + " for MvpView: " + delegateCallback.getMvpView());
            }

            if (mosbyViewId != null
                    && (presenter = PresenterManager.getPresenter(activity, mosbyViewId)) != null) {
                //
                // Presenter restored from cache
                //
                if (DEBUG) {
                    HiLog.error(LABEL,
                            "Reused presenter " + presenter + " for view " + delegateCallback.getMvpView());
                }
            } else {
                //
                // No presenter found in cache, most likely caused by process death
                //
                presenter = createViewIdAndCreatePresenter();
                if (DEBUG) {
                    HiLog.error(LABEL, "No presenter found although view Id was here: "
                            + mosbyViewId
                            + ". Most likely this was caused by a process death. New Presenter created"
                            + presenter
                            + " for view "
                            + getMvpView());
                }
            }
        } else {
            presenter = createViewIdAndCreatePresenter();
            if (DEBUG) {
                HiLog.error(LABEL, "New presenter " + presenter + " for view " + getMvpView());
            }
        }

        if (presenter == null) {
            StringBuilder sb = new StringBuilder();
            sb.append(MvpAbility.BASE_URL);
            sb.append(MvpAbility.ISSURE);
            throw new IllegalStateException(
                    "Oops, Presenter is null. This seems to be a Mosby internal bug. Please report this issue here: "+ sb.toString());
        }

        delegateCallback.setPresenter(presenter);
        getPresenter().attachView(getMvpView());

        if (DEBUG) {
            HiLog.error(LABEL, "View" + getMvpView() + " attached to Presenter " + presenter);
        }
    }

    @Override
    public void onPostActive() {

    }

    @Override
    public void onInactive() {

    }

    @Override
    public void onForeground(Intent intent) {

    }

    @Override
    public void onBackground() {

    }

    @Override
    public void onSaveAbilityState(PacMap outState) {
        if (keepPresenterInstance && outState != null) {
            outState.putString(KEY_MOSBY_VIEW_ID, mosbyViewId);
            if (DEBUG) {
                HiLog.error(LABEL,
                        "Saving MosbyViewId into Bundle. ViewId: " + mosbyViewId + " for view " + getMvpView());
            }
        }
    }

    @Override
    public void onRestoreAbilityState(PacMap inState) {
        if(inState != null && keepPresenterInstance){
            mosbyViewId = inState.getString(KEY_MOSBY_VIEW_ID);
        }

    }

    @Override
    public Object onStoreDataWhenConfigChange() {
        return null;
    }


}
